/*
 * MP3.c
 *
 * Created: 2012-06-11 19:21:33
 *  Author: Sebastian_2
 */ 

#include "MP3.h"
#include "misc.h"
#include "fonts/fonts.h"
#include <stdlib.h>

void MP3_ScanFiles() {
	FRESULT res;
	FIL file;
	FILINFO fno;
	DIR dir;
	WORD time, date;
	char timeC[16], dateC[16];
	
	// first off, read DATE file
	res = f_open(&file, "/music/DATE", FA_READ | FA_OPEN_EXISTING);
	if(res == FR_OK) {
		if(timeC == f_gets(timeC, sizeof(timeC), &file)
		&& dateC == f_gets(dateC, sizeof(dateC), &file)) {
			sscanf(timeC, "%d", (int*)&time);
			sscanf(dateC, "%d", (int*)&date);
		}
	} else {
		ERROR();
		return;
	}
	res = f_close(&file);
	CheckRes();
	
	// clear artist index file
	res = f_open(&file, "/music/IDX", FA_WRITE | FA_CREATE_ALWAYS); // opens and truncates the file
	CheckRes();
	res = f_close(&file);
	CheckRes();
	
	res = f_opendir(&dir, "/music");
	CheckRes();
	
	for(;;) {
		res = f_readdir(&dir, &fno);
		CheckRes();
		if(res != FR_OK) break; // error
		if(fno.fname[0] == '.') continue;
		if(fno.fname[0] == 0) { // end of items in folder
			res = f_open(&file, "/music/DATE", FA_WRITE | FA_CREATE_ALWAYS);
			CheckRes();
			if(f_printf(&file, "%d\n%d\n", time, date) == EOF) {
				ERROR();
				return;
			}
			res = f_close(&file);
			CheckRes();
			
			// we need to sort items in global index
			char tmp [ARTISTS_MAX][ARTISTS_LEN];
			uint8_t counter = 0;
			res = f_open(&file, "/music/IDX", FA_READ | FA_OPEN_EXISTING);
			CheckRes();
			while(f_gets(tmp[counter], ARTISTS_LEN, &file) == tmp[counter]) {
				counter++;
			}
			// now real sorting comes
			qsort(tmp, counter, ARTISTS_LEN, &stringArrayComparator);
			res = f_close(&file);
			CheckRes();
			res = f_open(&file, "/music/IDX", FA_WRITE | FA_CREATE_ALWAYS); // truncate file
			CheckRes();
			res = f_close(&file);
			CheckRes();
			res = f_open(&file, "/music/IDX", FA_WRITE | FA_OPEN_EXISTING);
			CheckRes();
			for(uint8_t i = 0; i < counter; i++) {
				if(f_puts(tmp[i], &file) == EOF) {
					ERROR();
					return;
				} 
			}
			res = f_close(&file);
			CheckRes();
			break;
		}
		
		if(fno.fattrib & AM_DIR)  // it's a dir
			MP3_ReadDirs(time, date, fno.fname);
	}
}

void MP3_ReadDirs(WORD time, WORD date, char *s) {
	FRESULT res;
	FIL file;
	FILINFO fno;
	DIR dir;
	char path[25];
	
	sprintf(path, "/music/%s", s);
	res = f_opendir(&dir, path);
	CheckRes();
	
	uint8_t dirCount = 0;	
	for(;;) { // iterate through files
		res = f_readdir(&dir, &fno);
		CheckRes();
		if(fno.fname[0] == '.') continue; // forgot what it meant(hidden folder maybe?)
		if(fno.fname[0] == 0 && dirCount >= 1) { // end of items in dir and at least one dir was scanned
			// add artist name to global IDX
			res = f_open(&file, "/music/IDX", FA_WRITE | FA_OPEN_EXISTING);
			CheckRes();
			res = f_lseek(&file, file.fsize);
			CheckRes();
			char tmp[25];
			char c = 0x03; // ETX
			sprintf(tmp, "%s%c%s\n", gVS1003FileInfo.artistName, c, s);
			if(f_puts(tmp, &file) == EOF) {
				ERROR();
				return;
			} 
			res = f_close(&file);
			CheckRes();
			break;
		}
		
		if(fno.fattrib & AM_DIR && fno.ftime > time && fno.fdate > date) { // it's a dir, and it's been modified after last scanning
			MP3_ReadFiles(time, date, dirCount == 0, s, fno.fname);
			dirCount++;
		}
	}
}

void MP3_ReadFiles(WORD time, WORD date, uint8_t clearIDX, char *artistDir, char *filesDir) {
	/* we're in folder that contains just audio files, no more stacked dirs.
	we assume that none of those files is already indexed. */
	
	FRESULT res;
	DIR dir;
	FIL file;
	FILINFO fno;
	char IDXpath[20];
	char path[40];
	char tmp[120];
	uint16_t bytesRead;
	uint8_t counter = 0;
	
	sprintf(path, "/music/%s/%s", artistDir, filesDir);
	res = f_opendir(&dir, path);
	CheckRes();
	
	
	for(;;) {
		res = f_readdir(&dir, &fno);
		CheckRes();
		if(fno.fname[0] == 0) { // end of files
			sprintf(IDXpath, "/music/%s/CNT", artistDir);
			res = f_open(&file, IDXpath, FA_WRITE | FA_OPEN_EXISTING);
			f_printf(&file, "%d", counter);
			f_close(&file);
			break;
		}
		if(fno.fattrib & AM_DIR) continue; // unlikely to happen, but it's better to be sure
		
		sprintf(path, "/music/%s/%s/%s", artistDir, filesDir, fno.fname);
		res = f_open(&file, path, FA_READ | FA_OPEN_EXISTING);
		if(res != FR_OK) continue;
		res = f_read(&file, &gVS1003Buffer1, VS1003_BUFSIZE, &bytesRead);
		if(res != FR_OK) continue;
		res = f_close(&file);
		if(res != FR_OK) continue;
		res = MP3_ReadTags();
		if(res != FR_OK) continue;
		counter++;
		
		// we have all info in the world we need, now it's time to write it to artist playlist
		sprintf(IDXpath, "/music/%s/IDX", artistDir);
		
		if(clearIDX) {
			res = f_open(&file, IDXpath, FA_WRITE | FA_CREATE_ALWAYS); // truncate file
			CheckRes();
			res = f_close(&file);
			CheckRes();
			sprintf(path, "/music/%s/CNT", artistDir);
			f_open(&file, path, FA_WRITE | FA_CREATE_ALWAYS);
			f_close(&file);
			clearIDX = 0;
		}
		
		res = f_open(&file, IDXpath, FA_WRITE | FA_OPEN_EXISTING);
		if(res != FR_OK) continue;
		res = f_lseek(&file, file.fsize); // move to the end of file
		CheckRes();
		char separator = 0x03; // ETX - end of text
		sprintf(tmp, "%s%c%s%c%s%c%lu\n", gVS1003FileInfo.artistName, separator, gVS1003FileInfo.songName, separator, 
		path, separator, gVS1003FileInfo.tagLenth);
		if(f_puts(tmp, &file) == EOF) {
			ERROR();
			return;
		} 
		res = f_close(&file);
		CheckRes();
	}
}

FRESULT MP3_ReadTags() {
	volatile uint32_t tagSize = 0;
	volatile uint32_t frameSize = 0;
	
	if(strncmp((char*)&gVS1003Buffer1, "ID3", 3) != 0) return FR_INVALID_OBJECT; // not a ID3v2 header
	if(gVS1003Buffer1[3] < 3) return FR_INVALID_OBJECT; // ID3v2 version is lower than 2.3
	if(gVS1003Buffer1[5] & 0x40) return FR_INVALID_OBJECT; // extended header is present
	
	tagSize = gVS1003Buffer1[9] & 0x7F;
	tagSize += (uint16_t)(gVS1003Buffer1[8] & 0x7F) << 7; // first bit is always 0, hence shifting 7 bits
	tagSize += (uint32_t)(gVS1003Buffer1[7] & 0x7F) << 14;
	tagSize += (uint32_t)(gVS1003Buffer1[6] & 0x7F) << 21;
	
	volatile uint16_t index = 10;
	uint8_t gotArtistName = 0;
	uint8_t gotSongTitle = 0;
	
	while(!(gotArtistName == 1 && gotSongTitle == 1)) {
		index += 4; // move beyond frame ID to read frame size
		frameSize = (uint32_t)(gVS1003Buffer1[index] & 0x7F) << 21;
		frameSize += (uint32_t)(gVS1003Buffer1[index+1] & 0x7F) << 14;
		frameSize += (uint32_t)(gVS1003Buffer1[index+2] & 0x7F) << 7;
		frameSize += gVS1003Buffer1[index+3] & 0x7F;
		index -= 4; // go back to frame ID
		
		if(strncmp((char*)&gVS1003Buffer1[index], "TIT2", 4) == 0){
			index += 10; // 10B of frame header
			char utf8_check = 0x03;
			if(strncmp((char*)&gVS1003Buffer1[index], &utf8_check, 1) == 0) {
				char tmp[70];
				uint16_t newLen = UTF8toASCII((char*)&gVS1003Buffer1[index+1], tmp, frameSize-1);
				tmp[newLen] = 0; // NULL at the end
				while(TextLength(tmp, &droidSans22ptFontInfo) > 300) {
					uint16_t len = strlen(tmp);
					tmp[len-1] = 0; // NULL
					tmp[len-2] = '.';
					tmp[len-3] = '.';
					tmp[len-4] = '.';
				}
				strcpy(gVS1003FileInfo.songName, tmp);
				gotSongTitle = 1;
			}
		} else if(strncmp((char*)&gVS1003Buffer1[index], "TPE1", 4) == 0) {
			index += 10; // 10B of frame header
			char utf8_check = 0x03;
			if(strncmp((char*)&gVS1003Buffer1[index], &utf8_check, 1) == 0) {
				char tmp[70];
				uint16_t newLen = UTF8toASCII((char*)&gVS1003Buffer1[index+1], tmp, frameSize-1);
				tmp[newLen] = 0; // NULL at the end
				while(TextLength(tmp, &droidSans22ptFontInfo) > 300) {
					uint16_t len = strlen(tmp);
					tmp[len-1] = 0; // NULL
					tmp[len-2] = '.';
					tmp[len-3] = '.';
					tmp[len-4] = '.';
				}
				strcpy(gVS1003FileInfo.artistName, tmp);
				gotArtistName = 1;
			}
		}
		index += frameSize;
		if(index > VS1003_BUFSIZE) // we're in serious trouble so we do the smart thing - run away
			break;
	}
	
	if(gotArtistName == 1 && gotSongTitle == 1) {
		gVS1003FileInfo.tagLenth = tagSize;
		return FR_OK;
	} else {
		return FR_INVALID_OBJECT;
	}
}